home *** CD-ROM | disk | FTP | other *** search
/ Komputer for Alle 1999 #5 / 1999 CD 5 (black).iso / Delphi3 / install / data.z / DIROUTLN.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-08-05  |  10.4 KB  |  379 lines

  1. unit DirOutln;
  2.  
  3. { Directory outline component }
  4.  
  5. interface
  6.  
  7. uses Classes, Forms, Controls, Outline, SysUtils, Graphics, Grids, StdCtrls,
  8.      Menus;
  9.  
  10. type
  11.   TTextCase = (tcLowerCase, tcUpperCase, tcAsIs);
  12.   TCaseFunction = function(const AString: string): string;
  13.  
  14.   TDirectoryOutline = class(TCustomOutline)
  15.   private
  16.     FDrive: Char;
  17.     FDirectory: TFileName;
  18.     FOnChange: TNotifyEvent;
  19.     FTextCase: TTextCase;
  20.     FCaseFunction: TCaseFunction;
  21.   protected
  22.     procedure SetDrive(NewDrive: Char);
  23.     procedure SetDirectory(const NewDirectory: TFileName);
  24.     procedure SetTextCase(NewTextCase: TTextCase);
  25.     procedure AssignCaseProc;
  26.     procedure BuildOneLevel(RootItem: Longint); virtual;
  27.     procedure BuildTree; virtual;
  28.     procedure BuildSubTree(RootItem: Longint); virtual;
  29.     procedure Change; virtual;
  30.     procedure Click; override;
  31.     procedure CreateWnd; override;
  32.     procedure Expand(Index: Longint); override;
  33.     function FindIndex(RootNode: TOutLineNode; SearchName: TFileName): Longint;
  34.     procedure Loaded; override;
  35.     procedure WalkTree(const Dest: string);
  36.   public
  37.     constructor Create(AOwner: TComponent); override;
  38.     function ForceCase(const AString: string): string;
  39.     property Drive: Char  read FDrive write SetDrive;
  40.     property Directory: TFileName  read FDirectory write SetDirectory;
  41.     property Lines stored False;
  42.   published
  43.     property Align;
  44.     property BorderStyle;
  45.     property Color;
  46.     property Ctl3D;
  47.     property DragCursor;
  48.     property DragMode;
  49.     property Enabled;
  50.     property Font;
  51.     property ItemHeight;
  52.     property OnChange: TNotifyEvent  read FOnChange write FOnChange;
  53.     property OnClick;
  54.     property OnCollapse;
  55.     property OnDblClick;
  56.     property OnDragDrop;
  57.     property OnDragOver;
  58.     property OnDrawItem;
  59.     property OnEndDrag;
  60.     property OnEnter;
  61.     property OnExit;
  62.     property OnExpand;
  63.     property OnKeyDown;
  64.     property OnKeyPress;
  65.     property OnKeyUp;
  66.     property OnMouseDown;
  67.     property OnMouseMove;
  68.     property OnMouseUp;
  69.     property OnStartDrag;
  70.     property Options default [ooStretchBitmaps, ooDrawFocusRect];
  71.     property ParentColor;
  72.     property ParentCtl3D;
  73.     property ParentFont;
  74.     property ParentShowHint;
  75.     property PictureClosed;
  76.     property PictureLeaf;
  77.     property PictureOpen;
  78.     property PopupMenu;
  79.     property ScrollBars;
  80.     property Style;
  81.     property ShowHint;
  82.     property TabOrder;
  83.     property TabStop;
  84.     property TextCase: TTextCase  read FTextCase write SetTextCase default tcLowerCase;
  85.     property Visible;
  86.   end;
  87.  
  88. function SameLetter(Letter1, Letter2: Char): Boolean;
  89.  
  90.  
  91. implementation
  92.  
  93. const
  94.   InvalidIndex = -1;
  95.  
  96. constructor TDirectoryOutline.Create(AOwner: TComponent);
  97. begin
  98.   inherited Create(AOwner);
  99.   PictureLeaf := PictureClosed;
  100.   Options := [ooDrawFocusRect];
  101.   TextCase := tcLowerCase;
  102.   AssignCaseProc;
  103. end;
  104.  
  105. procedure TDirectoryOutline.AssignCaseProc;
  106. begin
  107.   case TextCase of
  108.     tcLowerCase: FCaseFunction := AnsiLowerCaseFileName;
  109.     tcUpperCase: FCaseFunction := AnsiUpperCaseFileName;
  110.     else FCaseFunction := nil;
  111.   end;
  112. end;
  113.  
  114. type
  115.   PNodeInfo = ^TNodeInfo;
  116.   TNodeInfo = record
  117.     RootName: TFileName;
  118.     SearchRec: TSearchRec;
  119.     DosError: Integer;
  120.     RootNode: TOutlineNode;
  121.     TempChild, NewChild: Longint;
  122.   end;
  123.  
  124. function TDirectoryOutline.FindIndex(RootNode: TOutLineNode;
  125.   SearchName: TFileName): Longint;
  126. var
  127.   FirstChild, LastChild, TempChild: Longint;
  128. begin
  129.   FirstChild := RootNode.GetFirstChild;
  130.   if (FirstChild = InvalidIndex) or
  131.      (SearchName <= Items[FirstChild].Text) then
  132.        FindIndex := FirstChild
  133.   else
  134.   begin
  135.     LastChild := RootNode.GetLastChild;
  136.     if (SearchName >= Items[LastChild].Text) then
  137.       FindIndex := InvalidIndex
  138.     else
  139.     begin
  140.       repeat
  141.         TempChild := (FirstChild + LastChild) div 2; { binary search }
  142.         if (TempChild = FirstChild) then Inc(TempChild);
  143.         if (SearchName > Items[TempChild].Text) then
  144.           FirstChild := TempChild
  145.         else LastChild := TempChild
  146.       until FirstChild >= (LastChild - 1);
  147.       FindIndex := LastChild
  148.     end
  149.   end
  150. end;
  151.  
  152. procedure TDirectoryOutline.BuildOneLevel(RootItem: Longint);
  153. var
  154.   NodeInfo: PNodeInfo;
  155.   P: Integer;
  156. begin
  157.   New(NodeInfo);
  158.   try
  159.     with NodeInfo^ do
  160.     begin
  161.       RootName := Items[RootItem].FullPath;
  162.       P := AnsiPos(':\\', RootName);
  163.       if P <> 0 then System.Delete(RootName, P + 2, 1);
  164.       if (AnsiLastChar(RootName) <> '\') then
  165.         RootName := Concat(RootName, '\');
  166.       RootName := Concat(RootName, '*.*');
  167.       DosError := FindFirst(RootName, faDirectory, SearchRec);
  168.       while DosError = 0 do
  169.       begin
  170.         if (SearchRec.Attr and faDirectory = faDirectory) and (SearchRec.Name[1] <> '.') then
  171.         begin
  172.           SearchRec.Name := ForceCase(SearchRec.Name);
  173.           RootNode := Items[RootItem];
  174.           if RootNode.HasItems then { if has children, must alphabetize }
  175.           begin
  176.             TempChild := FindIndex(RootNode, SearchRec.Name);
  177.             if TempChild <> InvalidIndex then
  178.               NewChild := Insert(TempChild, SearchRec.Name)
  179.             else NewChild := Add(RootNode.GetLastChild, SearchRec.Name);
  180.           end
  181.           else NewChild := AddChild(RootItem, SearchRec.Name); { if first child, just add }
  182.         end;
  183.         DosError := FindNext(SearchRec);
  184.       end;
  185.     end;
  186.     Items[RootItem].Data := Pointer(1); { make non-nil so we know we've been here }
  187.   finally
  188.     Dispose(NodeInfo);
  189.   end;
  190. end;
  191.  
  192. procedure TDirectoryOutline.BuildTree;
  193. begin
  194.   Clear;
  195.   AddChild(0, ForceCase(Drive + ':\'));
  196.   WalkTree(FDirectory);
  197.   Change;
  198. end;
  199.  
  200. procedure TDirectoryOutline.BuildSubTree(RootItem: Longint);
  201. var
  202.   TempRoot: Longint;
  203.   RootNode: TOutlineNode;
  204. begin
  205.   BuildOneLevel(RootItem);
  206.   RootNode := Items[RootItem];
  207.   TempRoot := RootNode.GetFirstChild;
  208.   while TempRoot <> InvalidIndex do
  209.   begin
  210.     BuildSubTree(TempRoot);
  211.     TempRoot := RootNode.GetNextChild(TempRoot);
  212.   end;
  213. end;
  214.  
  215. procedure TDirectoryOutline.Change;
  216. begin
  217.   if Assigned(FOnChange) then FOnChange(Self);
  218. end;
  219.  
  220. procedure TDirectoryOutline.Click;
  221. var
  222.   P: Integer;
  223.   S: string;
  224. begin
  225.   inherited Click;
  226.   S := Items[SelectedItem].FullPath;
  227.   P := AnsiPos(':\\', S);
  228.   if P <> 0 then System.Delete(S, P + 2, 1);
  229.   Directory := S;
  230. end;
  231.  
  232. procedure TDirectoryOutline.CreateWnd;
  233. var
  234.   CurrentPath: string;
  235. begin
  236.   inherited CreateWnd;
  237.   if FDrive = #0 then
  238.   begin
  239.     GetDir(0, CurrentPath);
  240.     FDrive := ForceCase(CurrentPath)[1];
  241.     FDirectory := ForceCase(CurrentPath);
  242.   end;
  243.   if (not (csLoading in ComponentState)) and
  244.     (csDesigning in ComponentState) then BuildTree;
  245. end;
  246.  
  247. procedure TDirectoryOutline.Expand(Index: Longint);
  248. begin
  249.   if Items[Index].Data = nil then { if we've not previously expanded }
  250.     BuildOneLevel(Index);
  251.   inherited Expand(Index); { call the event handler }
  252. end;
  253.  
  254. function TDirectoryOutline.ForceCase(const AString: string): string;
  255. begin
  256.   if Assigned(FCaseFunction) then
  257.     Result := FCaseFunction(AString)
  258.   else Result := AString;
  259. end;
  260.  
  261. procedure TDirectoryOutline.Loaded;
  262. begin
  263.   inherited Loaded;
  264.   AssignCaseProc;
  265.   BuildTree;
  266. end;
  267.  
  268. procedure TDirectoryOutline.SetDirectory(const NewDirectory: TFileName);
  269. var
  270.   TempPath: TFileName;
  271. begin
  272.   if Length(NewDirectory) > 0 then  { ignore empty directory }
  273.   begin
  274.     if Copy(NewDirectory, Length(NewDirectory) - 1, 2) = ':\' then
  275.       TempPath := ForceCase(NewDirectory)
  276.     else
  277.       TempPath := ForceCase(ExpandFileName(NewDirectory)); { expand to full path }
  278.     if (Length(TempPath) > 3) and (AnsiLastChar(TempPath) = '\') then
  279.       SetLength(TempPath, Length(TempPath) - 1);
  280.     if AnsiCompareFileName(TempPath, FDirectory) <> 0 then { is it a dir change? }
  281.     begin
  282.       FDirectory := TempPath; { set new directory }
  283.       ChDir(FDirectory); { go there }
  284.       if TempPath[1] <> Drive then { check to see if we changed drives, too }
  285.         Drive := TempPath[1] { change drive/build list if needed }
  286.       else
  287.       begin
  288.         if Copy(FDirectory, Length(FDirectory) - 1, 2) = ':\' then
  289.           WalkTree(TempPath);
  290.         Change; { otherwise, we're done }
  291.       end;
  292.     end;
  293.   end;
  294. end;
  295.  
  296. procedure TDirectoryOutline.SetDrive(NewDrive: Char);
  297. var
  298.   TempPath: string;
  299. begin
  300.   if UpCase(NewDrive) in ['A'..'Z'] then { disallow all but drive letters}
  301.   begin
  302.     if (FDrive = #0) or not SameLetter(NewDrive, FDrive) then { update if no current drive or change }
  303.     begin
  304.       FDrive := NewDrive;
  305.       ChDir(FDrive + ':\');
  306.       GetDir(0, TempPath);
  307.       FDirectory := ForceCase(TempPath); { use correct case }
  308.       if not (csLoading in ComponentState) then BuildTree; { this ends up calling Change }
  309.     end;
  310.   end;
  311. end;
  312.  
  313. procedure TDirectoryOutline.SetTextCase(NewTextCase: TTextCase);
  314. var
  315.   CurrentPath: string;
  316. begin
  317.   if NewTextCase <> FTextCase then
  318.   begin
  319.     FTextCase := NewTextCase;
  320.     AssignCaseProc;
  321.     if NewTextCase = tcAsIs then
  322.     begin
  323.       GetDir(0, CurrentPath);
  324.       FDrive := CurrentPath[1];
  325.       FDirectory := CurrentPath;
  326.     end;
  327.     if not (csLoading in ComponentState) then BuildTree;
  328.   end;
  329. end;
  330.  
  331. procedure TDirectoryOutline.WalkTree(const Dest: string);
  332. var
  333.   TempPath, NextDir: TFileName;
  334.   SlashPos: Integer;
  335.   TempItem: Longint;
  336.  
  337.   function GetChildNamed(const Name: string): Longint;
  338.   begin
  339.     Items[TempItem].Expanded := True;
  340.     Result := Items[TempItem].GetFirstChild;
  341.     while Result <> InvalidIndex do
  342.     begin
  343.       if Items[Result].Text = Name then Exit;
  344.       Result := Items[TempItem].GetNextChild(Result);
  345.     end;
  346.   end;
  347.  
  348. begin
  349.   TempItem := 1; { start at root }
  350.   TempPath := ForceCase(Dest);
  351.   if Pos(':', TempPath) > 0 then
  352.     TempPath := Copy(TempPath, Pos(':', TempPath) + 1, Length(TempPath));
  353.   if TempPath[1] = '\' then System.Delete(TempPath, 1, 1);
  354.   NextDir := TempPath;
  355.   while Length(TempPath) > 0 do
  356.   begin
  357.     SlashPos := AnsiPos('\', TempPath);
  358.     if SlashPos > 0 then
  359.     begin
  360.       NextDir := Copy(TempPath, 1, SlashPos - 1);
  361.       TempPath := Copy(TempPath, SlashPos + 1, Length(TempPath));
  362.     end
  363.     else
  364.     begin
  365.       NextDir := TempPath;
  366.       TempPath := '';
  367.     end;
  368.     TempItem := GetChildNamed(NextDir);
  369.   end;
  370.   SelectedItem := TempItem;
  371. end;
  372.  
  373. function SameLetter(Letter1, Letter2: Char): Boolean;
  374. begin
  375.   Result := UpCase(Letter1) = UpCase(Letter2);
  376. end;
  377.  
  378. end.
  379.